package com.twu.biblioteca;

import java.io.IOException;
import java.util.Scanner;
import java.util.Stack;

public class BibliotecaApp {

    private UserInterface userInterface;

    private Stack<Controller> controllers = new Stack<Controller>();

    private BookShelf bookShelf;

    private UserRepository userRepository;

    private MovieRepository movieRepository;

    private InputDispatcher inputDispatcher;

    public static void main(String[] args) {
        BibliotecaApp app = new BibliotecaApp();
        UserInterface userInterface1 = new UserInterface();
        app.setUserInterface(userInterface1);

        BookShelf bookShelf1 = new BookShelf();
        bookShelf1.add("Test-Driven Development");
        bookShelf1.add("REST in Practice");
        app.setBookShelf(bookShelf1);

        MovieRepository movieRepository1 = new MovieRepository();
        movieRepository1.add(rated(new Movie("Gandhi", 1982, "Richard Attenborough"), 10));
        movieRepository1.add(new Movie("Sholay", 1975, "Ramesh Sippy"));
        app.setMovieRepository(movieRepository1);

        BookManager bookManager1 = new BookManager(bookShelf1);
        app.setInputDispatcher(app.new InputDispatcherImpl());

        UserRepository userRepository1 = new UserRepository();
        User user = new User("user");
        user.setPassword("pass");
        userRepository1.add(user);
        app.setUserRepository(userRepository1);

        SecurityContext securityContext = new SecurityContext();

        MenuController menuController1 = new MenuController(app);
        app.setInitialController(menuController1);

        LoginController loginController = new LoginController();
        loginController.setUserInterface(userInterface1);
        loginController.setUserRepository(userRepository1);
        loginController.setSecurityContext(securityContext);
        menuController1.setLoginController(loginController);
        menuController1.setSecurityContext(securityContext);

        ReserveController reserveController1 = new ReserveController();
        reserveController1.setUserInterface(userInterface1);
        reserveController1.setBookManager(bookManager1);
        menuController1.setReserveController(reserveController1);

        app.run();
    }

    private static final Movie rated(Movie movie, int rating) {
        movie.setRating(rating);
        return movie;
    }

    void setUserInterface(UserInterface userInterface) {
        this.userInterface = userInterface;
    }

    public UserInterface getUserInterface() {
        return userInterface;
    }

    public void setInitialController(Controller initialController) {
        controllers.clear();
        controllers.push(initialController);
    }

    public void setBookShelf(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    public BookShelf getBookShelf() {
        return bookShelf;
    }

    public void setInputDispatcher(InputDispatcher inputDispatcher) {
        this.inputDispatcher = inputDispatcher;
    }

    /* default */ void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /* default */ UserRepository getUserRepository() {
        return userRepository;
    }

    public void run() {
        userInterface.showWelcome();
        controllers.peek().enter();
        new Thread(inputDispatcher).run();
    }

    public void handle(String input) {
        Controller top = controllers.peek();
        Controller nextTop = top.handle(input);
        if (nextTop == null) {
            controllers.pop();
            if (controllers.isEmpty() == false) {
                controllers.peek().enter();
            }
        } else if (top.equals(nextTop) == false) {
            controllers.push(nextTop);
            nextTop.enter();
        }
    }

    public void quit() {
        try {
            inputDispatcher.close();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public MovieRepository getMovieRepository() {
        return movieRepository;
    }

    public void setMovieRepository(MovieRepository movieRepository) {
        this.movieRepository = movieRepository;
    }

    private final class InputDispatcherImpl implements InputDispatcher {

        private Scanner scanner;

        @Override
        public void run() {
            try {
                scanner = new Scanner(System.in);
                while (scanner.hasNextLine()) {
                    String input = scanner.nextLine();
                    BibliotecaApp.this.handle(input);
                }
            } catch (IllegalStateException ex) {
            } finally {
                scanner.close();
            }
        }

        @Override
        public void close() throws IOException {
            scanner.close();
        }
    }

}
